/**
 * 
 */
package org.ranch.api.service.impl;

import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.ranch.api.RanchConstants;
import org.ranch.api.security.util.RanchSecurityUtil;
import org.ranch.api.service.PersonService;
import org.ranch.api.service.UserService;
import org.ranch.api.utils.RanchStringUtil;
import org.ranch.dao.PersonDAO;
import org.ranch.dao.UserDAO;
import org.ranch.model.Gender;
import org.ranch.model.Person;
import org.ranch.model.RecordStatus;
import org.ranch.model.User;
import org.ranch.model.UserStatus;
import org.ranch.model.exception.RanchValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.genericdao.search.Filter;
import com.googlecode.genericdao.search.Search;

/**
 * Implementation of {@link PersonService}
 * 
 */
@Transactional
@Service("personService")
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonDAO personDAO;

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private UserService userService;

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    @Transactional(readOnly = true)
    public Person findPersonByFirstName(String firstName) {
	return personDAO.searchUniqueByPropertyEqual("firstName", firstName);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void savePerson(Person person) throws RanchValidationException {
	if (StringUtils.isEmpty(person.getId()) || StringUtils.isBlank(person.getId())) {
	    person.setFirstName(RanchStringUtil.capitalizeFirstLetters(person.getFirstName()));
	    person.setLastName(RanchStringUtil.capitalizeFirstLetters(person.getLastName()));
	}

	if (StringUtils.isBlank(person.getId()) || StringUtils.isEmpty(person.getId())) {
	    // check whether username exists
	    log.debug(this.getClass().getName() + " - checking for existing user");
	    User existingUser = userService.findUserByUsername(person.getUsername());
	    if (existingUser != null) {
		throw new RanchValidationException("a user with this username - "
			+ person.getUsername() + " already exists");
	    }

	} else {

	    if (StringUtils.isNotBlank(person.getUsername())) {
		User existingUserWithSimilarUsername = userService.findUserByUsername(person
			.getUsername());
		if (existingUserWithSimilarUsername.getId() != person.getId()) {
		    throw new RanchValidationException("a user with this username - "
			    + person.getUsername() + " already exists");
		}
	    }
	}

	RanchSecurityUtil.prepUserCredentials(person);

	personDAO.save(person);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Person> getPersons() {
	List<Person> persons = personDAO.searchByRecordStatus(RecordStatus.ACTIVE);
	Collections.sort(persons);
	return persons;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void deletePerson(Person Person) throws RanchValidationException {
	personDAO.delete(Person);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Person> findPersonByFirstname(String firstName) {
	Search search = new Search();
	search.addFilter(new Filter("firstName", firstName, Filter.OP_LIKE));
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	List<Person> persons = personDAO.search(search);

	Collections.sort(persons);
	return persons;
    }

    @Override
    @Transactional(readOnly = true)
    public Person getPersonById(String personId) {
	return personDAO.searchUniqueByPropertyEqual("id", personId);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Person> getPerson(Integer pageNo) {
	Search search = new Search();
	search.setMaxResults(RanchConstants.MAX_NUM_PAGE_RECORD);
	search.addSort("firstName", false, true);
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);

	/*
	 * if the page number is less than or equal to zero, no need for paging
	 */
	if (pageNo != null && pageNo > 0) {
	    search.setPage(pageNo - 1);
	} else {
	    search.setPage(0);
	}

	List<Person> persons = personDAO.search(search);
	Collections.sort(persons);
	return persons;
    }

    @Override
    public int getTotalNumberOfPerson() {
	Search search = new Search();
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	return personDAO.count(search);
    }

    @Override
    public int getNumberOfPersonsInSearch(String query) {
	Search search = prepareUserSearchByLoginName(query);
	return personDAO.count(search);
    }

    public static Search prepareUserSearchByLoginName(String query) {

	Search search = new Search();
	String param = new StringBuilder().append("%").append(StringEscapeUtils.escapeSql(query))
		.append("%").toString();
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	search.addFilterLike("firstName", param);
	search.addSort("firstName", false, true);

	return search;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public User validateUser(User user) throws RanchValidationException {
	// is new
	if (StringUtils.isBlank(user.getId()) || StringUtils.isEmpty(user.getId())) {
	    // check whether username exists
	    User existingUser = findUserByUsername(user.getUsername());
	    if (existingUser != null) {
		throw new RanchValidationException("a user with this username - "
			+ user.getUsername() + " already exists");
	    }

	    // by default, the user is set to active
	    user.setStatus(UserStatus.ENABLED);
	} else {
	    User existingUser = userDAO.find(user.getId());

	    if (existingUser != null) {
		user.setPassword(existingUser.getPassword());
		user.setSalt(existingUser.getSalt());
		user.setStatus(existingUser.getStatus());
	    }

	    User existingUserWithSimilarUsername = findUserByUsername(user.getUsername());
	    if (existingUserWithSimilarUsername.getId() != user.getId()) {
		throw new RanchValidationException("a user with this username - "
			+ user.getUsername() + " already exists");
	    }

	}

	// check for clear text password and
	// assign the hashedpassword and the salt from the old object
	RanchSecurityUtil.prepUserCredentials(user);
	return user;
    }

    public User findUserByUsername(String username) {
	return userDAO.searchUniqueByPropertyEqual("username", username);
    }

    @Override
    public Person findPersonById(String id) {
	return personDAO.searchUniqueByPropertyEqual("id", id);
    }

    @Override
    public void validatePerson(Person person) throws RanchValidationException {
	if (StringUtils.isBlank(person.getFirstName()))
	    throw new RanchValidationException("Supplied user is missing firstname");

	if (StringUtils.isBlank(person.getLastName()))
	    throw new RanchValidationException("Supplied user is missing lastname");

	if (person.getGender() == Gender.UNKNOWN)
	    throw new RanchValidationException("Supplied user is missing gender");

	if (StringUtils.isBlank(person.getNationality()))
	    throw new RanchValidationException("Supplied user is missing nationality");

	if (person.getResponsibility() == null)
	    throw new RanchValidationException("Supplied user's responsibility is missing");

	if (StringUtils.isBlank(person.getAddresses().getPhoneNo()))
	    throw new RanchValidationException("Supplied user is missing phoneNo");

	if (StringUtils.isBlank(person.getAddresses().getPhysicalAddress()))
	    throw new RanchValidationException("Supplied user is missing physical address");

	if (person.getDateOfBirth() == null)
	    throw new RanchValidationException("Supplied user is missing date of birth");

	if (person.getDateOfBirth().after(Calendar.getInstance().getTime())) {
	    throw new RanchValidationException(
		    "date of birth of the user cannot be in the future");
	}
	
	if (StringUtils.isEmpty(person.getClearTextPassword())) {
	    throw new RanchValidationException(
		    "The user's password field cannot be empty");
	}

	{
	    Calendar currentDateCalendar = Calendar.getInstance();
	    Calendar dobCalendar = Calendar.getInstance();
	    dobCalendar.setTime(person.getDateOfBirth());

	    String errorMsg = "the user is less than 16 years.";
	    int duration = currentDateCalendar.get(Calendar.YEAR) - dobCalendar.get(Calendar.YEAR);
	    if (duration < 16) {
		throw new RanchValidationException(errorMsg);
	    } else if (duration == 18) {
		if (dobCalendar.get(Calendar.MONTH) > currentDateCalendar.get(Calendar.MONTH)) {
		    throw new RanchValidationException(errorMsg);
		} else if ((dobCalendar.get(Calendar.MONTH) == currentDateCalendar
			.get(Calendar.MONTH))
			&& dobCalendar.get(Calendar.DAY_OF_MONTH) < currentDateCalendar
				.get(Calendar.DAY_OF_MONTH)) {
		    throw new RanchValidationException(errorMsg);
		}
	    }
	}
	
	
    }
}
